'''
在Python编程中，*args和**kwargs是两个非常强大的功能，它们允许开发者构建更加灵活和可扩展的函数。
*args用于在函数中传递不定数量的位置参数，而**kwargs允许传递不定数量的关键字参数。这两个功能的灵活性使得Python函数可以处理多种不同情况下的参数，极大地提高了代码的复用性和可读性。

对于中级和高级开发者而言，掌握*args和**kwargs的高级用法不仅可以简化代码编写过程，还能在遇到复杂的编程问题时提供更多的解决方案。
无论是在编写通用的工具函数、装饰器，还是在实现面向对象编程中的方法重载时，*args和**kwargs都扮演着不可或缺的角色。

在本教程中，我们将深入探讨*args和**kwargs的高级应用，从它们的基础用法开始，逐步过渡到更复杂的技巧和实践案例。
我们将通过具体的代码示例来演示这些高级技巧，帮助读者更好地理解和运用这两个功能，从而编写出更高效、更灵活的Python代码。

接下来，让我们从*args的使用和技巧开始，逐步深入了解这一强大的功能。
'''


# 在Python中，*args允许函数接收任意数量的位置参数，这意味着你可以在调用函数时传入任意多个没有明确指定的参数。这些参数会被封装进一个元组（tuple），在函数内部可以通过遍历这个元组来使用它们。
def add(*args):
    total = 0
    for num in args:
        total += num
    return total

if __name__ == '__main__':
    print(add(1,2,3))


# *args可以与普通的位置参数结合使用，但是普通的位置参数必须放在*args之前。
# 当然，*args只是形参名，可以任意取名，如*aaa
def greet(greetings, *aaa):
    print(f'{greetings}, {aaa}')

greet('Hello', 'Jack', 'Rose')



# 可以将*args用来将接收到的参数传递给另一个函数。
def multiply(*args):
    result = 1
    for number in args:
        result *= number
    return result

def wrapper_function(*args):
    print(f"Multiplication result: {multiply(args)}")  # 也可以在args前面加上*，这是一种解包动作，一样：multiply(*args)

wrapper_function(2, 3, 4)  # 输出：Multiplication result: 24



# 如果你有一个列表或元组，你可以在函数调用时使用*操作符来解包这个列表或元组，直接作为参数传入。
numbers = [1, 2, 3, 4]
print(add(*numbers))  # 输出：10




########################## **kwargs的使用和技巧 ############################
# **kwargs允许函数接收任意数量的关键字参数，这意味着你可以在调用函数时传入任意多个通过名字指定的参数。这些参数被封装成一个字典（dict），在函数内部可以通过键名来访问对应的值。
def introduce_yourself(**kwargs):
    print(f'type(kwargs)={type(kwargs)}')  # <class 'dict'>
    for key, value in kwargs.items():
        print(f"{key}: {value}")
introduce_yourself(name="John Doe", age=30, profession="Developer")


# **kwargs可以与固定参数一起使用，固定参数需要放在**kwargs之前。
def signup(username, password, **kwargs):
    print(f"Username: {username}, Password: {password}")
    for key, value in kwargs.items():
        print(f"{key}: {value}")
signup("john_doe", "123456", email="john@example.com", age=30)



# 可以将**kwargs用来将接收到的关键字参数传递给另一个函数。此处的**号表示将dict类型的对象解包
def display_info(**kwargs):
    for key, value in kwargs.items():
        print(f"{key}: {value}")

def wrapper_function(**kwargs):
    print("Displaying information:")
    display_info(**kwargs)

wrapper_function(name="Jane Doe", profession="Designer")






#################### 结合*args和**kwargs的高级应用 ###################
# 在Python中，装饰器是一种高级技术，允许你在不修改原有函数定义的情况下，增加函数的新功能。使用*args和**kwargs可以创建更通用的装饰器，适用于任意参数列表的函数。
def my_decorator(func):
    # 闭包
    def wrapper(*args, **kwargs):
        print("Something is happening before the function is called.")
        result = func(*args, **kwargs)
        print("Something is happening after the function is called.")
        return result
    return wrapper

@my_decorator
def say_hello(name):
    print(f"Hello {name}!")

say_hello("John")


# 在面向对象编程中，*args和**kwargs可以用于类的方法中，使得方法可以接受任意数量的参数，增强类的灵活性和复用性。
class Greeter:
    def greet(self, greetings, *args, **kwargs):
        print(greetings, ':')
        if args:
            print('args:')
            for arg in args:
                print(arg)
        if kwargs:
            print('kwargs:')
            for k,v in kwargs.items():
                print(f'k={k},v={v}')

g = Greeter()
g.greet('Hello', 'Jack', 'Rose', age=15, height=160, age2=16, height2=159)




###### 动态函数调用 ######
# *args和**kwargs可以用于动态地调用函数，这在编写需要高度灵活性的代码，如框架或库时非常有用。
def add(a, b):
    return a + b

def call_with_any_args(func, *args, **kwargs):
    return func(*args, **kwargs)

result = call_with_any_args(add, 2, 3)  # 传入位置参数
print(result)  # 输出：5


#




